/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2002-2006 * Sleepycat Software. All rights reserved. * * $Id: StatsFileReader.java,v 1.1 2006/05/06 08:59:59 ckaestne Exp $ */ package com.sleepycat.je.log; import java.io.IOException; import java.nio.ByteBuffer; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Comparator; import java.util.Iterator; import java.util.Map; import java.util.TreeMap; import com.sleepycat.je.DatabaseException; import com.sleepycat.je.config.EnvironmentParams; import com.sleepycat.je.dbi.EnvironmentImpl; import com.sleepycat.je.utilint.DbLsn; /** * The StatsFileReader generates stats about the log entries read, such as the * count of each type of entry, the number of bytes, minimum and maximum sized * log entry. */ public class StatsFileReader extends DumpFileReader { /* Keyed by LogEntryType, data is EntryInfo. */ private Map entryInfoMap; private long totalLogBytes; private long totalCount; /* Keep stats on log composition in terms of ckpt intervals. */ private ArrayList ckptList; private CheckpointCounter ckptCounter; private long firstLsnRead; /** * Create this reader to start at a given LSN. */ public StatsFileReader(EnvironmentImpl env, int readBufferSize, long startLsn, long finishLsn, String entryTypes, String txnIds, boolean verbose) throws IOException, DatabaseException { super(env, readBufferSize, startLsn, finishLsn, entryTypes, txnIds, verbose); entryInfoMap = new TreeMap(new LogEntryTypeComparator()); totalLogBytes = 0; totalCount = 0; ckptCounter = new CheckpointCounter(); ckptList = new ArrayList(); if (verbose) { ckptList.add(ckptCounter); } } /** * This reader collects stats about the log entry. */ protected boolean processEntry(ByteBuffer entryBuffer) throws DatabaseException { /* * Record various stats based on the entry header, then move the buffer * forward to skip ahead. */ LogEntryType lastEntryType = LogEntryType.findType(currentEntryTypeNum, currentEntryTypeVersion); entryBuffer.position(entryBuffer.position() + currentEntrySize); /* * Get the info object for it, if this is the first time it's seen, * create an info object and insert it. */ EntryInfo info = (EntryInfo) entryInfoMap.get(lastEntryType); if (info == null) { info = new EntryInfo(); entryInfoMap.put(lastEntryType, info); } /* Update counts. */ info.count++; totalCount++; if (LogEntryType.isProvisional(currentEntryTypeVersion)) { info.provisionalCount++; } int size = currentEntrySize + LogManager.HEADER_BYTES; info.totalBytes += size; totalLogBytes += size; if ((info.minBytes==0) || (info.minBytes > size)) { info.minBytes = size; } if (info.maxBytes < size) { info.maxBytes = size; } if (verbose) { if (firstLsnRead == DbLsn.NULL_LSN) { firstLsnRead = getLastLsn(); } if (currentEntryTypeNum == LogEntryType.LOG_CKPT_END.getTypeNum()) { /* start counting a new interval */ ckptCounter.endCkptLsn = getLastLsn(); ckptCounter = new CheckpointCounter(); ckptList.add(ckptCounter); } else { ckptCounter.increment(this, currentEntryTypeNum); } } return true; } public void summarize() { System.out.println("Log statistics:"); Iterator iter = entryInfoMap.entrySet().iterator(); NumberFormat form = NumberFormat.getIntegerInstance(); NumberFormat percentForm = NumberFormat.getInstance(); percentForm.setMaximumFractionDigits(1); System.out.println(pad("type") + pad("total") + pad("provisional") + pad("total") + pad("min") + pad("max") + pad("avg") + pad("entries")); System.out.println(pad("") + pad("count") + pad("count") + pad("bytes") + pad("bytes") + pad("bytes") + pad("bytes") + pad("as % of log")); long realTotalBytes = 0; while (iter.hasNext()) { Map.Entry m = (Map.Entry) iter.next(); EntryInfo info = (EntryInfo) m.getValue(); StringBuffer sb = new StringBuffer(); LogEntryType entryType =(LogEntryType) m.getKey(); sb.append(pad(entryType.toString())); sb.append(pad(form.format(info.count))); sb.append(pad(form.format(info.provisionalCount))); sb.append(pad(form.format(info.totalBytes))); sb.append(pad(form.format(info.minBytes))); sb.append(pad(form.format(info.maxBytes))); sb.append(pad(form.format((long)(info.totalBytes/info.count)))); double entryPercent = ((double)(info.totalBytes *100)/totalLogBytes); sb.append(pad(percentForm.format(entryPercent))); System.out.println(sb.toString()); /* Calculate key/data size for transactional LN */ if (entryType == LogEntryType.LOG_LN_TRANSACTIONAL) { /* LN_TX entry overhead: 8 bytes node id 1 byte boolean (whether data exists or is null) 4 bytes data size 4 bytes key size 4 bytes database id 8 bytes abort LSN 1 byte abortKnownDeleted 8 bytes txn id 8 bytes lastlogged LSN (backpointer for txn) */ int overhead = LogManager.HEADER_BYTES + 46; realTotalBytes += info.totalBytes-(info.count*overhead); } /* Calculate key/data size for non-transactional LN */ if (entryType == LogEntryType.LOG_LN) { /* LN_TX entry overhead: 8 bytes node id 1 byte boolean (whether data exists or is null) 4 bytes data size 4 bytes key size 4 bytes database id */ int overhead = LogManager.HEADER_BYTES + 21; realTotalBytes += info.totalBytes-(info.count*overhead); } } /* Print special line for key/data */ StringBuffer sb = new StringBuffer(); sb.append(pad("key/data")); sb.append(pad("")); sb.append(pad("")); sb.append(pad(form.format(realTotalBytes))); sb.append(pad("")); sb.append(pad("")); sb.append(pad("")); String realSize = "(" + percentForm.format((double)(realTotalBytes*100)/ totalLogBytes) + ")"; sb.append(pad(realSize)); System.out.println(sb.toString()); System.out.println("\nTotal bytes in portion of log read: " + form.format(totalLogBytes)); System.out.println("Total number of entries: " + form.format(totalCount)); if (verbose) { summarizeCheckpointInfo(); } } private String pad(String result) { int spaces = 15 - result.length(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < spaces; i++) { sb.append(" "); } sb.append(result); return sb.toString(); } private void summarizeCheckpointInfo() { System.out.println("\nPer checkpoint interval info:"); /* * Print out checkpoint interval info. * If the log looks like this: * * start of log * ckpt1 start * ckpt1 end * ckpt2 start * ckpt2 end * end of log * * There are 3 ckpt intervals * start of log->ckpt1 end * ckpt1 end -> ckpt2 end * ckpt2 end -> end of log */ System.out.println(pad("lnTxn") + pad("ln") + pad("mapLNTxn") + pad("mapLN") + pad("end-end") + // ckpt n-1 end -> ckpt n end pad("end-start") + // ckpt n-1 end -> ckpt n start pad("start-end") + // ckpt n start -> ckpt n end pad("maxLNReplay") + pad("ckptEnd")); long logFileMax; try { logFileMax = env.getConfigManager().getLong( EnvironmentParams.LOG_FILE_MAX); } catch (DatabaseException e) { e.printStackTrace(); return; } Iterator iter = ckptList.iterator(); CheckpointCounter prevCounter = null; NumberFormat form = NumberFormat.getInstance(); while (iter.hasNext()) { CheckpointCounter c = (CheckpointCounter)iter.next(); StringBuffer sb = new StringBuffer(); /* Entry type counts. */ int maxTxnLNs = c.preStartLNTxnCount + c.postStartLNTxnCount; sb.append(pad(form.format(maxTxnLNs))); int maxLNs = c.preStartLNCount + c.postStartLNCount; sb.append(pad(form.format(maxLNs))); sb.append(pad(form.format(c.preStartMapLNTxnCount + c.postStartMapLNTxnCount))); sb.append(pad(form.format(c.preStartMapLNCount + c.postStartMapLNCount))); /* Checkpoint interval distance. */ long end = (c.endCkptLsn == DbLsn.NULL_LSN) ? getLastLsn() : c.endCkptLsn; long endToEndDistance = 0; FileManager fileManager = env.getFileManager(); if (prevCounter == null) { endToEndDistance = DbLsn.getWithCleaningDistance(end, fileManager, firstLsnRead, logFileMax); } else { endToEndDistance = DbLsn.getWithCleaningDistance(end, fileManager, prevCounter.endCkptLsn, logFileMax); } sb.append(pad(form.format(endToEndDistance))); /* * Interval between last checkpoint end and * this checkpoint start. */ long start = (c.startCkptLsn == DbLsn.NULL_LSN) ? getLastLsn() : c.startCkptLsn; long endToStartDistance = 0; if (prevCounter == null) { endToStartDistance = DbLsn.getWithCleaningDistance(start, fileManager, firstLsnRead, logFileMax); } else { endToStartDistance = DbLsn.getWithCleaningDistance(start, fileManager, prevCounter.endCkptLsn, logFileMax); } sb.append(pad(form.format(endToStartDistance))); /* * Interval between ckpt start and ckpt end. */ long startToEndDistance = 0; if ((c.startCkptLsn != DbLsn.NULL_LSN) && (c.endCkptLsn != DbLsn.NULL_LSN)) { startToEndDistance = DbLsn.getWithCleaningDistance(c.endCkptLsn, fileManager, c.startCkptLsn, logFileMax); } sb.append(pad(form.format(startToEndDistance))); /* * The maximum number of LNs to replay includes the portion of LNs * from checkpoint start to checkpoint end of the previous * interval. */ int maxReplay = maxLNs + maxTxnLNs; if (prevCounter != null) { maxReplay += prevCounter.postStartLNTxnCount; maxReplay += prevCounter.postStartLNCount; } sb.append(pad(form.format(maxReplay))); if (c.endCkptLsn == DbLsn.NULL_LSN) { sb.append(" ").append(DbLsn.getNoFormatString(getLastLsn())); } else { sb.append(" ").append(DbLsn.getNoFormatString(c.endCkptLsn)); } System.out.println(sb.toString()); prevCounter = c; } } static class EntryInfo { public int count; public int provisionalCount; public long totalBytes; public int minBytes; public int maxBytes; EntryInfo() { count = 0; provisionalCount = 0; totalBytes = 0; minBytes = 0; maxBytes = 0; } } static class LogEntryTypeComparator implements Comparator { public int compare(Object o1, Object o2) { if (o1 == null) { return -1; } if (o2 == null) { return 1; } if (o1 instanceof LogEntryType && o2 instanceof LogEntryType) { Byte t1 = new Byte(((LogEntryType) o1).getTypeNum()); Byte t2 = new Byte(((LogEntryType) o2).getTypeNum()); return t1.compareTo(t2); } else { throw new IllegalArgumentException ("non LogEntryType passed to LogEntryType.compare"); } } } /* * Accumulate the count of items from checkpoint end->checkpoint end. */ static class CheckpointCounter { public long startCkptLsn = DbLsn.NULL_LSN; public long endCkptLsn = DbLsn.NULL_LSN; public int preStartLNTxnCount; public int preStartLNCount; public int preStartMapLNTxnCount; public int preStartMapLNCount; public int postStartLNTxnCount; public int postStartLNCount; public int postStartMapLNTxnCount; public int postStartMapLNCount; public void increment(FileReader reader, byte currentEntryTypeNum) { if (currentEntryTypeNum == LogEntryType.LOG_CKPT_START.getTypeNum()) { startCkptLsn = reader.getLastLsn(); } else if (currentEntryTypeNum == LogEntryType.LOG_LN_TRANSACTIONAL.getTypeNum()) { if (startCkptLsn == DbLsn.NULL_LSN) { preStartLNTxnCount++; } else { postStartLNTxnCount++; } } else if (currentEntryTypeNum == LogEntryType.LOG_LN.getTypeNum()) { if (startCkptLsn == DbLsn.NULL_LSN) { preStartLNCount++; } else { postStartLNCount++; } } else if (currentEntryTypeNum == LogEntryType.LOG_MAPLN.getTypeNum()) { if (startCkptLsn == DbLsn.NULL_LSN) { preStartMapLNCount++; } else { postStartMapLNCount++; } } else if (currentEntryTypeNum == LogEntryType.LOG_MAPLN_TRANSACTIONAL.getTypeNum()) { if (startCkptLsn == DbLsn.NULL_LSN) { preStartMapLNTxnCount++; } else { postStartMapLNTxnCount++; } } } } }